package com.wss.lsl.test.driven.redis;

import java.util.List;
import java.util.concurrent.TimeUnit;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

/**
 * TODO: 增加描述
 * 
 * @author wei.ss
 * @date 2017年12月12日
 * @copyright wonhigh.cn
 */
public class DistributedLock {

	private JedisPool jedisPool;

	public DistributedLock(JedisPool jedisPool) {

		this.jedisPool = jedisPool;
	}
	
	private String getLockKey(String key){
		
		return "JedisLocked_" + key;
	}

	/**
	 * 获取锁
	 * 
	 * @param key		锁键	
	 * @param value		锁的值。是一个唯一的值，每次调用应该不同
	 * @param lockTime	锁定的时间。单位是秒
	 * @param waitTime	等待的时间。单位是秒
	 * @return
	 * @author wei.ss
	 */
	public boolean acquireLock(String key, String value, int lockTime,
			int waitTime) {
		
		key = getLockKey(key);
		long begin = System.currentTimeMillis();
		long endTime = begin + waitTime * 1000;

		boolean locked = false;
		String result = null;
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			
			while (System.currentTimeMillis() < endTime) {
				result = jedis.set(key, value, "NX", "EX", lockTime);
				if ("OK".equals(result)) {
					locked = true;
					break;
				}
				TimeUnit.MILLISECONDS.sleep(10);
			}
			
			// 防止key死锁，设置过期时间
			if(!locked) {
				Long ttl = jedis.ttl(key);
				if(null != ttl && ttl == -1) {
					jedis.expire(key, lockTime);
				}
			}
		} catch (Exception e) {
			e.printStackTrace(); // 获取分布式锁发生异常
		}

		return locked;
	}

	/**
	 * 释放锁
	 * 
	 * @param key
	 * @param value
	 * @author wei.ss
	 */
	public void releaseLock(String key, String value) {
		
		key = getLockKey(key);
		Transaction transaction = null;
		Jedis jedis = null;
		while(true) {
			try {
				jedis = jedisPool.getResource();
				jedis.watch(key); // 监控key
				String oldValue = jedis.get(key);
				if (oldValue == null || !value.equals(oldValue)) {
					jedis.unwatch();
					// 锁已经过期了，或者被其他客户端获取了
					return;
				}
				
				transaction = jedis.multi();// 开启事务
				transaction.del(key);
				List<Object> result = transaction.exec(); // 提交事务
				if(null == result || result.isEmpty()) {
					// 事务执行失败，重试
					continue;
				}
			} catch (Exception e) {
				// 释放锁失败
				e.printStackTrace();
			}
		}
	}
}
